package com.zzyl.nursing.service.impl;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertDataMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IDeviceDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.service.IAlertRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * 报警规则Service业务层处理
 * 
 * @author ruoyi
 * @date 2025-06-12
 */
@Service
@Slf4j
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService
{
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private IAlertDataService alertDataService;

    /**
     * 查询报警规则
     * 
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     * 
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule)
    {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule)
    {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule)
    {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则
     * 
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则信息
     * 
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id)
    {
        return removeById(id) ? 1 : 0;
    }

    /**
     * 报警规则过滤
     */

    @Override
    public void alertFilter() {
        //查询所有报警规则，判断是否为空
        LambdaQueryWrapper<AlertRule> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(AlertRule::getStatus,1);
        List<AlertRule> ruleList = this.list(wrapper1);
        if(CollUtil.isEmpty(ruleList)){
            return;
        }
        //从redis中查询所有上报的数据，判断是否为空

        List<Object> allDeviceDataList = redisTemplate.opsForHash().values("iot:device_latest_data");
        if(CollUtil.isEmpty(allDeviceDataList)){
            return;
        }

        List<DeviceData> deviceDataList = new ArrayList<>();
        //遍历从redis从查询到的设备数据，就可得到每一条设备的数据，然后放进集合中
        allDeviceDataList.forEach(deviceData ->{
            List<DeviceData> dataList = JSONUtil.toList(deviceData.toString(), DeviceData.class);
            deviceDataList.addAll(dataList);
        });

        //遍历设备数据，得到每条设备数据，然后把设备数据和每一个规则进行匹配
        deviceDataList.forEach(deviceData -> {
            //查询该产品物模型下所有的规则
            LambdaQueryWrapper<AlertRule> wrapper2 = new LambdaQueryWrapper<>();
            wrapper2.eq(AlertRule::getProductKey,deviceData.getProductKey())
                    .eq(AlertRule::getFunctionId,deviceData.getFunctionId())
                    .eq(AlertRule::getIotId,"-1")
                    .eq(AlertRule::getStatus,1);
            List<AlertRule> ruleList1 = this.list(wrapper2);

            //查询该产品物模型下对应设备的规则
            LambdaQueryWrapper<AlertRule> wrapper3 = new LambdaQueryWrapper<>();
            wrapper3.eq(AlertRule::getProductKey,deviceData.getProductKey())
                    .eq(AlertRule::getFunctionId,deviceData.getFunctionId())
                    .eq(AlertRule::getIotId,deviceData.getIotId())
                    .eq(AlertRule::getStatus,1);
            List<AlertRule> ruleList2 = this.list(wrapper3);
            Collection<AlertRule> alertRules = CollUtil.addAll(ruleList1, ruleList2);
            if(CollUtil.isEmpty(alertRules)){
                return;
            }
            alertRules.forEach(alertRule -> {
                alertRuleMatchDeviceData(alertRule,deviceData);
            });

        });

    }

    /**
     * 报警规则匹配设备数据
     * @param alertRule 报警规则
     * @param deviceData 设备数据
     */

    private void alertRuleMatchDeviceData(AlertRule alertRule, DeviceData deviceData) {
        //判断是否在生效时间内
        LocalTime alarmTime = deviceData.getAlarmTime().toLocalTime();
        String alertEffectivePeriod = alertRule.getAlertEffectivePeriod();
        String[] split = alertEffectivePeriod.split("~");
        LocalTime startDate = LocalTime.parse(split[0]);
        LocalTime endDate = LocalTime.parse(split[1]);
        if(alarmTime.isBefore(startDate) || alarmTime.isAfter(endDate)){
            return;
        }
        //判断数据是否满足阈值规则,满足则报警
        String alertRuleCountKey = "alert_rule_count:" + alertRule.getProductKey() +":"+ alertRule.getIotId() +":"+ alertRule.getFunctionId() +":"+ alertRule.getAlertRuleName();
        Double dataValue = Double.valueOf(deviceData.getDataValue());//设备数据值
        Double value = alertRule.getValue();//报警规则阈值
        int compare = CompareUtil.compare(dataValue, value);
        if((alertRule.getOperator().equals(">=") && compare >= 0) || (alertRule.getOperator().equals("<") && compare < 0) ){
            log.info("当前上报数据符合当前规则，可以报警");
        }else{
            //todo 删除redis报警数
            redisTemplate.delete(alertRuleCountKey);
            return;
        }

        //判断沉默周期:原理:当报警成功的时候，会向redis中添加一个沉默周期key,从redis中获取该沉默周期key，如果该key没有失效，则还处于沉默周期，则不进行报警
        String alertRuleSilentPeriodKey = "silent_period:" + alertRule.getProductKey() +":"+ alertRule.getIotId() +":"+ alertRule.getFunctionId() +":"+ alertRule.getAlertRuleName();
        String s = redisTemplate.opsForValue().get(alertRuleSilentPeriodKey);
        if(StringUtils.isNotEmpty(s)){
            return;
        }

        //判断持续周期:当第一次报警成功，报警次数设置为1，之后，每次报警成功，次数加1，等到成功次数等于持续周期，则进行报警，并重置报警次数
        String alertRuleCountValue  = redisTemplate.opsForValue().get(alertRuleCountKey);//定义一个报警次数key，用来记录报警次数
        int count;
        if(StringUtils.isEmpty(alertRuleCountValue)){
            count = 1;
        }else{
            count =Integer.parseInt(alertRuleCountValue)+1;
        }
        String duration = alertRule.getDuration().toString();//获取当前规则的持续周期
        if(count!=Integer.parseInt(duration)){//判断当前报警次数是否等于持续周期,如果不等,报警次数加1,并返回
            redisTemplate.opsForValue().set(alertRuleCountKey,String.valueOf(count));
            return;
        }
        //报警成功，需重置报警次数，并添加沉默周期
        redisTemplate.delete(alertRuleCountKey);
        redisTemplate.opsForValue().set(alertRuleSilentPeriodKey,"1",alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);//给当前成功报警的规则的沉默周期设置一个有失效时间的key,来判断是否在沉默周期内

        //查询出设备绑定的用户信息:

        //判断是否是老人数据
        Integer alertDataType = alertRule.getAlertDataType();
        List<Long> userIds;
        if(alertDataType==0){//老人异常数据
            //判断是否是固定设备(绑定床位)
            String iotId = deviceData.getIotId();
            Integer locationType = deviceData.getLocationType();
            if(locationType==1){
                //是固定设备,就是绑定床位
                userIds = deviceMapper.selectNursingIdByBedIotId(iotId);
            }else{
                //是随身设备,就是绑定老人
                userIds =  deviceMapper.selectNursingIdByElderIotId(iotId);
            }
        }else{//设备异常数据
            //查询维修工
            userIds =  deviceMapper.selectNursingIdByRoleName("维修工");
        }
        List<Long> managerIds =  deviceMapper.selectNursingIdByRoleName("超级管理员");
        Collection<Long> collection =  CollUtil.addAll(userIds, managerIds);
        ArrayList<Long> allUserIds = CollUtil.distinct(collection);
        insertAlertData(allUserIds, alertRule,deviceData);
    }

    private void insertAlertData(ArrayList<Long> allUserIds, AlertRule alertRule, DeviceData deviceData) {
        //1.插入数据的条数应与userIds的个数一致,每条数据除了userId外，其他字段都一样
        List<AlertData> alertDataList = new ArrayList<>();
        allUserIds.forEach(userId -> {
            AlertData alertData = new AlertData();
            BeanUtils.copyProperties(deviceData, alertData);
            alertData.setAlertRuleId(alertRule.getId());
            String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", alertRule.getFunctionName(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration());
            alertData.setAlertReason(alertReason);
            alertData.setType(alertRule.getAlertDataType());
            alertData.setStatus(0);
            alertData.setCreateTime(new Date());
            alertData.setUpdateTime(new Date());
            alertData.setAccessLocation(deviceData.getAccessLocation());
            alertData.setId(null);
            alertData.setUserId(userId);
            alertDataList.add(alertData);
        });

        alertDataService.saveBatch(alertDataList);
    }
}
